// yarn add gulp --dev

const { src, dest, parallel, series, watch } = require('gulp') // destnation目标位置

// yarn add del --dev 非gulp插件,用来清理文件
const del = require('del')

// 热更新开发服务器，减少重复操作，提高开发效率
// yarn add browser-sync --dev 模块会提供开发服务器，支持热更新到浏览器中
const browserSync = require('browser-sync')

// yarn add gulp-load-plugins --dev 引入太多，自动加载全部plugins
const loadPlugins = require('gulp-load-plugins')

const plugins = loadPlugins()
const bs = browserSync.create() // 创建服务器

const cwd = process.cwd()// 当前工作目录
let config = {
  // default config
}
try {
  const loadConfig = require(`${cwd}/pages.config.js`)
  config = Object.assign({},config, loadConfig)
} catch (e) {}

const clean = () => {
  return del(['dist', 'temp']) // 返回promise
}

const style = () => {
  return src('src/assets/styles/*.scss', { base: 'src' }) // 指定选项参数：基准路径src（输出时会把src后面的目录保留下来）
    .pipe(plugins.sass({ outputStyle: 'expanded' })) // 每个插件都是个函数，调用函数返回一个文件转化流. outputStyle将样式代码{ }完全展开，yarn add gulp-sass --dev
    .pipe(dest('temp')) // temp 临时输出的文件夹
    .pipe(bs.reload({ stream: true })) // 把内部文件流信息推到浏览器，当然也可以不加，通过bs.init files配置

  // 注意： _开头的css文件会人为是内部引用文件不会被构建出来
}

const script = () => {
  return src('src/assets/scripts/*.js', { base: 'src' })
    .pipe(plugins.eslint())
    .pipe(plugins.eslint.format())
    .pipe(plugins.eslint.failAfterError())
    .pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
    .pipe(dest('temp'))
    .pipe(bs.reload({ stream: true }))
  // babel不加预设，gulp转换会失败。babel只是个转换平台提供个环境，具体转换通过babel内部插件，而preset就是插件的集合，preset-env就是最新特性的整体打包
  // 或者单独添加个.babelrc
  // yarn add gulp-babel @babel/core @babel/preset-env --dev
}

const page = () => {
  return src('src/*.html', { base: 'src' }) // 'src/**/*.html'  src任意子目录下的html文件
    .pipe(plugins.swig({ data: config.data, defaults: { cache: false } })) // cache防止模板缓存导致页面不能及时更新
    .pipe(dest('temp'))
    .pipe(bs.reload({ stream: true }))
    // yarn add gulp-swig --dev （swig模板引擎）html文件
    // yarn add bootstrap --dev 此html使用了bootstrap
}

const image = () => {
  return src('src/assets/images/**', { base: 'src' })
    .pipe(plugins.imagemin()) // 图片无损压缩
    .pipe(dest('dist'))

  // yarn add gulp-imgmin --dev
}

const font = () => {
  return src('src/assets/fonts/**', { base: 'src' })
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
  // 字体文件一般是拷贝过去。不需要处理
}

const extra = () => {
  return src('public/**', { base: 'public' })
    .pipe(dest('dist'))
    // 额外的文件，public文件
}

const serve = () => {
  // dist必须存在，才能使用borswer-sync对其进行热更新。因此serve前要先执行compile

  // gulp Api watch 会自动监视 源文件 路径的通配符，文件变化后，会重新执行某个任务，覆盖掉dist下的文件，从而browser-sync监视到dist下变化更新页面
  watch('src/assets/styles/*.scss', style)// 参数1：通配符,参数2：任务
  watch('src/assets/scripts/*.js', script)
  watch('src/*.html', page)
  // 开发阶段监视的越多构建开销越大。项目一般只是在上线之前压缩图片或者字体，在不变的情况下并不需要一直构建到dist,修改baseDir，添加develop串行任务
  // watch('src/assets/images/**',image)
  // watch('src/assets/fonts/**',font)
  // watch('public/**',extra)

  // 当文件发生变化时更新浏览器
  watch([
    'src/assets/images/**',
    'src/assets/fonts/**',
    'public/**'
  ], bs.reload) // 只有发生变化才通过browser-sync模块提供的 bs.reload到浏览器，这三个只在build上线前编译一次

  bs.init({ // 初始化web服务器配置
    notify: false, // 关闭启动时显示的brower-sync启动
    port: 2080, // 默认3000
    // open:false, //自动打开浏览器
    // files:'dist/**', //路径通配符，监听 打包后的dist文件 改变并热更 /给任务添加pipe(bs.reload({ stream:true } )) 流式输出也可以
    server: {
      baseDir: ['temp', 'src', 'public'], // 网站根目录"dist",如果改成数组，从前到后文件内找上面监视的内容
      routes: { // 添加一个路由，解决dist/index.html中link 找不到node_modules/内样式的问题，让代码中的指向根目录下的node_modules
        '/node_modules': 'node_modules' // 键请求前缀，值是相对根路径，类似于，指定开发阶段html里的node_mdle指向根路径下的node_mdle
        // 然而生产阶段不能使用路由的方法，html中link内容如node_modules取不到可以通过，yarn add gulp-useref --dev 对它进行查找合并
      }
      // routes优先于baseDir先运行
    }
  })
}

const useref = () => {
  // 文件引用处理，解决文件内的引用路径找不到，对引用路径进行输出的插件
  // 上线前，取到dist中html中的link特殊注释内的css，js文件，进行useref打包编译。在compile后执行，不然没有注释link文件没法useref解析
  // 把注释为 build:css ~ endbuild 中的文件(中间多个link会合并)，打包成新的文件在dist中生成，dist中的html再引入这个新的文件就能找到了
  // yarn add gulp-useref --dev
  return src('temp/*.html', { base: 'temp' })
    .pipe(plugins.useref({ searchPath: ['temp', '.'] })) // 查找注释的路径， link引入了nodex_modules从根下找，如果配置了当前目录cwd，从cwd下找，注意的配置的searchPath路径
    // 对useref合并过程中新生成的html js css压缩 yarn add gulp-htmlmin gulp-uglify gulp-clean-css --dev
    // 三种流，需要判断是哪种则作对应操作 yarn add gulp-if --dev
    .pipe(plugins.if(/\.js$/, plugins.uglify()))
    .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
    .pipe(plugins.if(/\.html$/, plugins.htmlmin({ collapseWhitespace: true, minifyCSS: true, minifyJS: true })))
    .pipe(dest('dist'))
}

const compile = parallel(style, script, page) // 没啥关联，并行

// 上线之前执行的任务
const build = series(
  clean,
  parallel(
    series(compile, useref),
    image,
    font,
    extra
  )
)

const develop = series(compile, serve)

module.exports = {
  // style,  //注意。如果源文件带"_"开头，会忽略不会被转换，会认为它是主文件依赖的文件
  // script,
  // page,
  // image,
  // font,
  // serve,
  // useref, //去除特定的注释，可以将HTML引用的多个CSS和JS合并起来，减小依赖的文件个数，从而减少浏览器发起的请求次数,它只负责合并，不负责压缩
  // compile,
  clean, // 哪些任务需要对外暴露，添加到npm scripts,gitignore添加忽略
  build,
  develop
  // script
}
// 当然项目复用不可能每次把这个代码段copy一下，一旦出现插件修复升级或其他问题，那代码段则无法使用。因此，需要封装自动化构建工作流
// 解决办法：创建一个模块，发布到npm仓库，再使用这个模块，模块里封装gulpfile
//    Gulpfile+Gulp = 构建工作流
//    Gulpfile+Gulp CLI = mcgee-pages
